package net.kindroid.fm;

import java.lang.ref.WeakReference;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.fmradio.FmReceiver;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.AudioSystem;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.PowerManager.WakeLock;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Toast;

public class FMService extends Service
{
	private FmReceiver mFM = null;
	public static boolean isActivityFocus = false;
	public static boolean isFMState = false;
	public static boolean isCallState = false;
	public static boolean isMusicVolumeFocus = false;
	public static final String CLOSE_FM = "net.kindroid.CLOSE_FM";
	public static final String NOTIFY_STATE_CHANGE = "net.kindroid.NOTIFY_STATE_CHANGE";
	public static final String ALARM_FM_SWITCH = "net.kindroid.ALARM_FM_SWITCH";
	public static final String FM_CHANNEL_SWITCH = "net.kindroid.FM_CHANNEL_SWITCH";
	public static final String SCREEN_ON = "android.intent.action.SCREEN_ON";
	public static final String SCREEN_OFF = "android.intent.action.SCREEN_OFF";
	public static final String OPEN_DEVICE_ERROR = "isOpenError";
	private int channel;
	private int volume;
	private int mCurrentVolume = 0;
	private boolean isAlarmState;
	private boolean isScreenOn = true;

	private static final int FADEIN = 1;
	private static final int MUSIC_VOLUME_ACTIVE = FADEIN + 1;
	public static final int PLAYBACKSERVICE_STATUS = MUSIC_VOLUME_ACTIVE + 1;

	/**
	 * wake lock
	 */
	private WakeLock cpuLock = null;

	/**
	 * power manager
	 */
	private PowerManager pm = null;

	private void startAndFadeIn()
	{
		mFMHandler.sendEmptyMessageDelayed(FADEIN, 10);
	}

	private Handler mFMHandler = new Handler()
	{
		@Override
		public void handleMessage(Message msg)
		{
			switch (msg.what)
			{
				case FADEIN:
					if (mFM != null)
					{
						mCurrentVolume++;
						if (mCurrentVolume <= volume)
						{
							mFM.setVolume(mCurrentVolume);
							mFMHandler.sendEmptyMessageDelayed(FADEIN, 100);
						}
					}
					break;
				case MUSIC_VOLUME_ACTIVE:
					if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC))
					{
						if (!isMusicVolumeFocus)
						{
							closeVoice();
							notifyStateChange();
							deleteNotification();
						}
						isMusicVolumeFocus = true;
					} else if (isMusicVolumeFocus && !isCallState)
					{
						requestAudioFocus();
						setVolume();
						showNotification();
						isMusicVolumeFocus = false;
					}

					if (mFM != null && isScreenOn)
					{
						mFMHandler.sendEmptyMessageDelayed(MUSIC_VOLUME_ACTIVE, 500);
					}
					break;
				default:
					break;
			}
		}
	};

	@Override
	public void onCreate()
	{
		registerReceivers();
		pm = (PowerManager) this.getSystemService(Context.POWER_SERVICE);
		cpuLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "FM");
		if (cpuLock != null)
		{
			cpuLock.acquire();
		}
		TelephonyManager tmgr = (TelephonyManager) getSystemService("phone");
		if (tmgr != null)
		{
			tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
		}
		// if(TelephonyManager.getPhoneCount() > 1)
		// {
		// TelephonyManager tmgr2 = (TelephonyManager)
		// getSystemService("phone1");
		// if (tmgr2 != null)
		// {
		// tmgr2.listen(mPhoneStateListener2,
		// PhoneStateListener.LISTEN_CALL_STATE);
		// }
		// }
		super.onCreate();
	}

	@Override
	public void onDestroy()
	{
		releaseFM();
		closeVoice();
		unregisterReceiver(headSetReceiver);
		abandonAudioFocus();
		FMEntryView.isOutVoice = false;
		if (cpuLock != null)
		{
			cpuLock.release();
		}
		super.onDestroy();
	}

	@Override
	public void onStart(Intent intent, int startId)
	{
		if (intent != null)
		{
			channel = intent.getIntExtra("fmChannel", 0);
			isAlarmState = intent.getBooleanExtra("isAlarmState", false);
			if (channel > 0)
			{
				try
				{
					if (isAlarmState)
					{
						boolean b = new MyServiceimpl(this).openFM();
						setAlarmVolume();
					}
				} catch (RemoteException e)
				{
				}
			}
		}
		super.onStart(intent, startId);
	}

	private void releaseFM()
	{
		if (mFM != null)
		{
			mFM.disable();
			mFM.release();
			mFM = null;
		}
	}

	private void registerReceivers()
	{
		registerReceiver(headSetReceiver, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
		registerReceiver(headSetReceiver, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
		registerReceiver(headSetReceiver, new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
		registerReceiver(headSetReceiver, new IntentFilter(Intent.ACTION_SHUTDOWN));
		registerReceiver(headSetReceiver, new IntentFilter(ALARM_FM_SWITCH));
		registerReceiver(headSetReceiver, new IntentFilter(FM_CHANNEL_SWITCH));
		registerReceiver(headSetReceiver, new IntentFilter(CLOSE_FM));
		registerReceiver(headSetReceiver, new IntentFilter(SCREEN_ON));
		registerReceiver(headSetReceiver, new IntentFilter(SCREEN_OFF));
	}

	private void requestAudioFocus()
	{
		AudioManager audiomanager = (AudioManager) getSystemService(AUDIO_SERVICE);
		audiomanager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_FM, AudioManager.AUDIOFOCUS_GAIN);
	}

	private void abandonAudioFocus()
	{
		AudioManager audiomanager = (AudioManager) getSystemService(AUDIO_SERVICE);
		audiomanager.abandonAudioFocus(mAudioFocusListener);
	}

	private void closeVoice()
	{
		Intent intent = new Intent();
		intent.setAction(Intent.ACTION_FM);
		intent.putExtra("state", 0);
		intent.putExtra("speaker", 0);
		sendBroadcast(intent);
		mCurrentVolume = 0;
	}

	private void openDevice()
	{
		try
		{
			// two parameters is always null
			mFM = new FmReceiver("/dev/QN8085_FM", null);
		} catch (InstantiationException e)
		{
		}

		if (mFM != null)
		{
			mFMHandler.sendEmptyMessage(MUSIC_VOLUME_ACTIVE);
		}
	}

	@Override
	public IBinder onBind(Intent intent)
	{
		return new MyServiceimpl(this);
	}

	public class MyServiceimpl extends IFMService.Stub
	{
		WeakReference<FMService> mService;

		MyServiceimpl(FMService service)
		{
			mService = new WeakReference<FMService>(service);
		}

		@Override
		public boolean openFM() throws RemoteException
		{
			return mService.get().openFM();
		}

		@Override
		public boolean closeFM() throws RemoteException
		{
			return mService.get().closeFM();
		}

		@Override
		public boolean isOpen() throws RemoteException
		{
			return mService.get().isOpen();
		}

		@Override
		public boolean searchStation(int start, int direction, int timeout, int reserve) throws RemoteException
		{
			return mService.get().searchStation(start, direction, timeout, reserve);
		}

		@Override
		public int getCurrentFrequency() throws RemoteException
		{
			return mService.get().getTunedFrequency();
		}

		@Override
		public boolean stopSearch() throws RemoteException
		{
			return mService.get().stopSearch();
		}

		@Override
		public boolean setVolume(int value) throws RemoteException
		{
			return mService.get().setVolume(value);
		}

		@Override
		public int getCurrentVolume() throws RemoteException
		{
			return mService.get().getCurrentVolume();
		}

		@Override
		public int setMuteMode(int mode) throws RemoteException
		{
			return mService.get().setMuteMode(mode);
		}

		@Override
		public int setCurrentFrequency(int frequency) throws RemoteException
		{
			return mService.get().setCurrentFrequency(frequency);
		}
	}

	/**
	 * headset listener
	 */
	private final BroadcastReceiver headSetReceiver = new BroadcastReceiver()
	{
		public void onReceive(Context context, Intent intent)
		{
			String action = intent.getAction();
			if (action.equals(Intent.ACTION_HEADSET_PLUG))
			{
				// headphone plugged
				if (intent.getIntExtra("state", 0) == 0)
				{
					if (mFM != null)
					{
						if (FMService.isActivityFocus)
						{
							// show dialog inform user insert headset
							Intent requireHeadSet = new Intent(context, RequireHeadSetView.class);
							requireHeadSet.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
							startActivity(requireHeadSet);
						} else
						{
							Toast.makeText(context, context.getText(R.string.requireheadset_toast), Toast.LENGTH_SHORT).show();
						}
					} else if (FMService.isActivityFocus)
					{
						// show dialog inform user insert headset
						Intent requireHeadSet = new Intent(context, RequireHeadSetView.class);
						requireHeadSet.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
						startActivity(requireHeadSet);
					}
					closeIntent();
					sendBroadcast(new Intent("com.android.deskclock.search_done"));
				}
			} else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED))
			{
				Toast.makeText(context, context.getText(R.string.status_airplanemode), Toast.LENGTH_SHORT).show();
				closeIntent();
			} else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION))
			{
				if (intent.hasExtra("android.media.EXTRA_VOLUME_STREAM_TYPE") && intent.hasExtra("android.media.EXTRA_VOLUME_STREAM_VALUE"))
				{
					if (intent.getIntExtra("android.media.EXTRA_VOLUME_STREAM_TYPE", -1) == AudioManager.STREAM_FM
							|| intent.getIntExtra("android.media.EXTRA_VOLUME_STREAM_TYPE", -1) == AudioManager.STREAM_MUSIC)
					{
						int fm_volume = intent.getIntExtra("android.media.EXTRA_VOLUME_STREAM_VALUE", -1);
						if (fm_volume != -1)
						{
							if (mFM != null)
							{
								mFM.setVolume(fm_volume);
								Log.e("setVolume", "setVolume.....fm_volume = " + fm_volume);
							}
						}
					}
				}

			} else if (action.equals(CLOSE_FM) || action.equals(Intent.ACTION_SHUTDOWN))
			{
				releaseFM();
			} else if (action.equals(ALARM_FM_SWITCH))
			{
				if (isFMState)
				{
					requestAudioFocus();
					sendBroadcast(new Intent(FM_CHANNEL_SWITCH));
					setVolume();
				} else
				{
					closeIntent();
					closeVoice();
				}
			} else if (action.equals(SCREEN_ON))
			{
				isScreenOn = true;
				mFMHandler.sendEmptyMessage(MUSIC_VOLUME_ACTIVE);
			} else if (action.equals(SCREEN_OFF))
			{
				isScreenOn = false;
			}
		}
	};

	private void closeIntent()
	{
		Intent close_intent = new Intent();
		close_intent.setAction(CLOSE_FM);
		sendBroadcast(close_intent);
		stopSelf();
	}

	public int setCurrentFrequency(int frequency)
	{
		if (mFM == null)
		{
			return -1;
		}

		int result = mFM.setStation(frequency) ? 1 : -1;
		return result;
	}

	public int setMuteMode(int mode)
	{
		if (mFM == null)
		{
			return -1;
		}

		int result = mFM.setMuteMode(mode) ? 1 : -1;
		return result;
	}

	public int getCurrentVolume()
	{
		return 0;
	}

	public boolean setVolume(int value)
	{
		if (mFM == null)
		{
			return false;
		}

		return mFM.setVolume(value);
	}

	public boolean stopSearch()
	{
		if (mFM == null)
		{
			return false;
		}

		return mFM.cancelSearch();
	}

	public int getTunedFrequency()
	{
		if (mFM == null)
		{
			return -1;
		}

		return mFM.getTunedFrequency();
	}

	public boolean searchStation(int start, int direction, int timeout, int reserve)
	{
		if (mFM == null)
		{
			return false;
		}
		return mFM.searchStations(start, direction, timeout, reserve);
	}

	public boolean isOpen()
	{
		boolean result = false;
		if (mFM != null)
		{
			result = true;
		} else
		{
			result = false;
		}
		return result;
	}

	public boolean closeFM()
	{

		if (mFM == null)
		{
			return false;
		}

		mFM.disable();
		mFM.release();
		mFM = null;
		deleteNotification();

		return true;
	}

	public boolean openFM()
	{
		requestAudioFocus();
		if (mFM == null)
		{
			try
			{
				// two parameters is always null
				mFM = new FmReceiver("/dev/QN8085_FM", null);
			} catch (InstantiationException e)
			{
//				 Intent close_intent = new Intent();
//				 close_intent.setAction(CLOSE_FM);
//				 close_intent.putExtra(OPEN_DEVICE_ERROR, true);
//				 sendBroadcast(close_intent);
//				 stopSelf();

				return false;
			}
		}
		mFM.enable(null);
		if (channel >= 875 && channel <= 1080)
		{
			AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
			int fm_volume = am.getStreamVolume(AudioManager.STREAM_FM);
			mFM.setStation(channel);
			mFM.setVolume(fm_volume);
		}
		if (mFM != null)
		{
			mFMHandler.sendEmptyMessage(MUSIC_VOLUME_ACTIVE);
		}
		if (isFMState)
		{
			showNotification();
		}

		return true;
	}

	/**
	 * set current fm volume sync with framework
	 */
	private void setVolume()
	{
		AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
		int fm_volume = am.getStreamVolume(AudioManager.STREAM_FM);
		volume = fm_volume;
		mCurrentVolume = 0;
		startAndFadeIn();
		Intent intent = new Intent();
		intent.setAction(Intent.ACTION_FM);
		intent.putExtra("state", 1);
		intent.putExtra("speaker", FMEntryView.isOutVoice ? 1 : 0);
		sendBroadcast(intent);
	}

	/**
	 * set Alarm volume
	 */
	private void setAlarmVolume()
	{
		AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
		int fm_volume = am.getStreamVolume(AudioManager.STREAM_ALARM);
		volume = fm_volume;
		startAndFadeIn();
		Intent intent = new Intent();
		intent.setAction(Intent.ACTION_FM);
		intent.putExtra("state", 1);
		intent.putExtra("speaker", 1);
		sendBroadcast(intent);
	}

	private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener()
	{
		public void onAudioFocusChange(int focusChange)
		{
			switch (focusChange)
			{
				case AudioManager.AUDIOFOCUS_GAIN:
				{
					if (mFM == null)
					{
						openDevice();
					}
					showNotification();
					requestAudioFocus();
					setVolume();
					break;
				}
				case AudioManager.AUDIOFOCUS_LOSS:
				{
					closeVoice();
					deleteNotification();
					break;
				}
				case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
				case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
				{
					closeVoice();
					// if(isCallState)
					// {
					// releaseFM();
					// }
					releaseFM();
					deleteNotification();
					break;
				}
			}
		}
	};

	private PhoneStateListener mPhoneStateListener = new PhoneStateListener()
	{
		@Override
		public void onCallStateChanged(int state, String incomingNumber)
		{
			if (state == TelephonyManager.CALL_STATE_RINGING)
			{
				isCallState = true;
			} else if (state == TelephonyManager.CALL_STATE_OFFHOOK)
			{
				isCallState = true;
			} else if (state == TelephonyManager.CALL_STATE_IDLE)
			{
				isCallState = false;
				notifyStateChange();
			}
		}
	};

	private PhoneStateListener mPhoneStateListener2 = new PhoneStateListener()
	{
		@Override
		public void onCallStateChanged(int state, String incomingNumber)
		{
			if (state == TelephonyManager.CALL_STATE_RINGING)
			{
				isCallState = true;
			} else if (state == TelephonyManager.CALL_STATE_OFFHOOK)
			{
				isCallState = true;
			} else if (state == TelephonyManager.CALL_STATE_IDLE)
			{
				isCallState = false;
				notifyStateChange();
			}
		}
	};

	private void notifyStateChange()
	{
		if (FMService.isActivityFocus)
		{
			sendBroadcast(new Intent(NOTIFY_STATE_CHANGE));
		}
	}

	/**
	 * notification
	 * 
	 * @param id
	 */
	private void showNotification()
	{
		RemoteViews views = new RemoteViews(getPackageName(), R.layout.statusbar);
		views.setImageViewResource(R.id.icon, R.drawable.fm_icon);
		views.setTextViewText(R.id.fm_run, getString(R.string.fm_run));
		Notification status = new Notification();
		status.contentView = views;
		status.flags |= Notification.FLAG_ONGOING_EVENT;
		status.icon = R.drawable.ic_fm_status_bar;
		status.contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, FMEntryView.class).addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT), 0);
		startForeground(PLAYBACKSERVICE_STATUS, status);
	}

	/**
	 * delete notification
	 */
	private void deleteNotification()
	{
		NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		nm.cancel(PLAYBACKSERVICE_STATUS);
		stopForeground(true);
	}
}
